#include "header.h"

void *read(void *ptr)
{
	int sockDesc = (int) ptr;

	//printf("Read thread created with sockDesc = %d\n",sockDesc);
	
	/*char buffer[50];
	memset(buffer,'\0',20);
	int b = recv(sockDesc,&buffer,50,0);
	//printf("READ sock = %d BUffer = %c bytes read=%d\n",sockDesc,buffer[0],b);
	*/

	
	struct timeval selectTimeOut;
	selectTimeOut.tv_sec = 1;
	selectTimeOut.tv_usec = 0; 
	
	fd_set readfds;
	
	int rv;

	struct timeval keepAliveTimeFirst, keepAliveTimeLast;
	gettimeofday(&keepAliveTimeFirst, NULL);

	
	while(1)
	{
		gettimeofday(&keepAliveTimeLast, NULL);

		FD_ZERO(&readfds);
		FD_SET(sockDesc, &readfds);	

		if(shutdownFlag == true || joinToTheNetworkFlag == true)
		{
			
			pthread_mutex_lock(&WriteQueue[sockDesc]);

			pthread_cond_signal(&condition_var_Write[sockDesc]);						//signal write thread to awake if it is on condition variable						
			pthread_mutex_unlock(&WriteQueue[sockDesc]);
			printf("Read Thread EXIT due to Shutdown or soft restart or Shutdown\n");
			pthread_exit(NULL);
		}

		pthread_mutex_lock(&deleteConnectionLock[sockDesc]);				
		if(deleteConnection[sockDesc] == true)
		{
			pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);	

			pthread_mutex_lock(&WriteQueue[sockDesc]);
			pthread_cond_signal(&condition_var_Write[sockDesc]);						//signal write thread to awake if it is on condition variable						
			pthread_mutex_unlock(&WriteQueue[sockDesc]);

			pthread_mutex_lock(&NeighborList);																//Apply LOCK
			multimap<string,int> tempMultiMap = node.getNeighborList();
			multimap<string,int>::iterator it;
			for(it = tempMultiMap.begin(); it!=tempMultiMap.end(); it++)
			{
				if((*it).second == sockDesc)
				{
					tempMultiMap.erase(it);
				}
			}
			node.setNeighborList(tempMultiMap);
			
			if(IsBeaconNode == false)
			{
				int totalNeighbors = node.getNeighborList().size();
				if( totalNeighbors < (int)node.getMinNeighbors())								
				{
					printf("joinToTheNetworkFlag is SET - 2\n");
					joinToTheNetworkFlag = true;
				}
			}
			
			pthread_mutex_unlock(&NeighborList);																//RELEASE LOCK

			
			printf("1 - Read Thread EXIT at socket = %d\n",sockDesc);
			pthread_exit(NULL);
		}
		pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);				

		printf("Keep Alive time diff = %u\n",(uint32_t)(keepAliveTimeLast.tv_sec - keepAliveTimeFirst.tv_sec));
		printf("Keep Alive time = %u\n",node.getKeepAliveTimeout());
		if ((uint32_t)(keepAliveTimeLast.tv_sec - keepAliveTimeFirst.tv_sec) >= node.getKeepAliveTimeout())			//check for keepalive time out
		{
			printf("KeepAlive time out took place\n");

			pthread_mutex_lock(&WriteQueue[sockDesc]);
			pthread_cond_signal(&condition_var_Write[sockDesc]);						//signal write thread to awake if it is on condition variable						
			pthread_mutex_unlock(&WriteQueue[sockDesc]);

			pthread_mutex_lock(&deleteConnectionLock[sockDesc]);
			deleteConnection[sockDesc] = true;
			pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);


																											//remove the neighbor from neighbor datastructure
			pthread_mutex_lock(&NeighborList);																//Apply LOCK
			multimap<string,int> tempMultiMap = node.getNeighborList();
			multimap<string,int>::iterator it;
			for(it = tempMultiMap.begin(); it!=tempMultiMap.end(); it++)
			{
				if((*it).second == sockDesc)
				{
					tempMultiMap.erase(it);
				}
			}
			node.setNeighborList(tempMultiMap);
			if(IsBeaconNode == false)
			{
				int totalNeighbors = node.getNeighborList().size();
				if( totalNeighbors < (int)node.getMinNeighbors())								
				{
					printf("joinToTheNetworkFlag is SET - 1\n");
					joinToTheNetworkFlag = true;
				}
			}

			pthread_mutex_unlock(&NeighborList);																//Apply LOCK
			
			printf("2 - Read Thread EXIT at socket = %d\n",sockDesc);			
			pthread_exit(NULL);
			
		}
																									
		
		rv = select(sockDesc+1, &readfds, NULL, NULL, &selectTimeOut);

		if(rv == -1) 
		{
			perror("select 1\n"); // error occurred in select()
			pthread_mutex_lock(&deleteConnectionLock[sockDesc]);				
			deleteConnection[sockDesc] = true;
			pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);	
			printf("3 - read thread EXIT due to select = -1\n");
			pthread_exit(NULL);
		}
		else if(rv == 0)
		{																						// check if this connection is duplicated

			pthread_mutex_lock(&NeighborList);																//Apply LOCK
			list<int> mylist = node.getRemoveConnectionList();					
			pthread_mutex_unlock(&NeighborList);	
			//Release LOCK
			for (list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it)
			{
				if((*it) == sockDesc)																	//if true							
				{
					mylist.remove(sockDesc);
					node.setRemoveConnectionList(mylist);

					pthread_mutex_lock(&WriteQueue[sockDesc]);
					pthread_cond_signal(&condition_var_Write[sockDesc]);						//signal write thread to awake if it is on condition variable						
					pthread_mutex_unlock(&WriteQueue[sockDesc]);
	
					pthread_mutex_lock(&deleteConnectionLock[sockDesc]);
					deleteConnection[sockDesc] = true;
					pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);

					pthread_mutex_lock(&ReadQueue);																//APPLY LOCK
					pthread_cond_signal( &condition_var_Read);											//send signal to corresponding write thread
					pthread_mutex_unlock(&ReadQueue);																//APPLY LOCK

					//printf("*********READ THREAD EXIT DUE TO DUPLICATE CONNECTION*******\n");
					pthread_mutex_unlock(&NeighborList);																//Release LOCK

					printf("4 - Read Thread EXIT at socket = %d\n",sockDesc);
					pthread_exit(NULL);
				}
			}

			continue;
		}

		//else if (rv > 0)
		//{
			// there is data in our socket descriptor
		//	if(FD_ISSET(sockDesc,&readfds))
		//	{

				
				
																											// RECEIVE HEADER
				Header recHeader;

				char recHeaderBuffer[28];
				memset(recHeaderBuffer,'\0',28);														
				int recHeaderBytes = recv(sockDesc,recHeaderBuffer,27,0);
				if(recHeaderBytes != 27)
				{
					//printf("Header was not completely received in READ\n");
					//printf("Header----------------->%s bytes received=%d\n",recHeaderBuffer,recHeaderBytes);

					pthread_mutex_lock(&WriteQueue[sockDesc]);
					pthread_cond_signal(&condition_var_Write[sockDesc]);						//signal write thread to awake if it is on condition variable						
					pthread_mutex_unlock(&WriteQueue[sockDesc]);
	
					pthread_mutex_lock(&deleteConnectionLock[sockDesc]);
					deleteConnection[sockDesc] = true;
					pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);
					
					pthread_mutex_lock(&NeighborList);																//Apply LOCK
					multimap<string,int> tempMultiMap = node.getNeighborList();
					multimap<string,int>::iterator it;
					for(it = tempMultiMap.begin(); it!=tempMultiMap.end(); it++)
					{
						if((*it).second == sockDesc)
						{
							tempMultiMap.erase(it);
						}
					}
					node.setNeighborList(tempMultiMap);
					if(IsBeaconNode == false)
					{
						int totalNeighbors = node.getNeighborList().size();
						
						//printf("totalNeighbors =%d\n",totalNeighbors);
						//printf("min = %d\n",node.getMinNeighbors());
						if( totalNeighbors < (int)node.getMinNeighbors())								
						{
							//printf("Flag set\n");
							printf("joinToTheNetworkFlag is SET - 3\n");
							joinToTheNetworkFlag = true;
						}
					}

					pthread_mutex_unlock(&NeighborList);	
					printf("5 - Read Thread EXIT at socket = %d\n",sockDesc);
					pthread_exit(NULL);
				}
				recHeaderBuffer[strlen(recHeaderBuffer)+1] = '\0';
				//printf("Header Received\n");


				recHeader.uoid = (unsigned char *)malloc(20* sizeof(char));
				memcpy(&recHeader.messageType,&ntohs(recHeaderBuffer[0]),1);
				memcpy(recHeader.uoid,&recHeaderBuffer[1],20);
				memcpy(&recHeader.ttl,&ntohs(recHeaderBuffer[21]),1);
				memcpy(&recHeader.reserved,&ntohs(recHeaderBuffer[22]),1);
				memcpy(&recHeader.dataLength,&ntohl(recHeaderBuffer[23]),4);

				if(recHeader.messageType == keepAliveMessageType)
				{
					
					Messages msg;msg.Header = (char *)malloc(28*sizeof(char));
					memset(msg.Header,'\0',28);
					msg.Data = (char *)malloc(512*sizeof(char));
					memset(msg.Data,'\0',512);
					memcpy(msg.Header,&recHeader,27);

					struct timeval t;
					gettimeofday(&t, NULL);
					enterLogMessage(msg,'r',t);
					printf("Keep Alive msg received\n");
					
				}

								
				/*printf("Type = 0x%02x\n",recHeader.messageType);
				printUOID(recHeader.uoid);
				printf("TTL = %hhu\n",recHeader.ttl);
				printf("Reserved = %hhu\n",recHeader.reserved);
				printf("Datalength = %u\n",recHeader.dataLength);
				*/	
				gettimeofday(&keepAliveTimeFirst, NULL);	// as msg received the time will start from this point
				
				if( recHeader.dataLength > 0 )
				{
																										//RECEIVE  MESSAGE
					char recMessageBuffer[recHeader.dataLength+1];
					memset(recMessageBuffer,'\0',(recHeader.dataLength+1)*sizeof(char));
					int recMsgBytes = recv(sockDesc,recMessageBuffer,recHeader.dataLength,0);	
					if(recMsgBytes != (int) recHeader.dataLength)
					{
						//printf("Msg was not completely received in READ\n");

						pthread_mutex_lock(&WriteQueue[sockDesc]);
						pthread_cond_signal(&condition_var_Write[sockDesc]);						//signal write thread to awake if it is on condition variable						
						pthread_mutex_unlock(&WriteQueue[sockDesc]);

						pthread_mutex_lock(&deleteConnectionLock[sockDesc]);
						deleteConnection[sockDesc] = true;
						pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);
						
						pthread_mutex_lock(&NeighborList);																//Apply LOCK
						multimap<string,int> tempMultiMap = node.getNeighborList();
						multimap<string,int>::iterator it;
						for(it = tempMultiMap.begin(); it!=tempMultiMap.end(); it++)
						{
							if((*it).second == sockDesc)
							{
								tempMultiMap.erase(it);
							}
						}
						node.setNeighborList(tempMultiMap);
						if(IsBeaconNode == false)
						{
							int totalNeighbors = node.getNeighborList().size();
							if( totalNeighbors < (int)node.getMinNeighbors())								
							{
								printf("joinToTheNetworkFlag is SET - 4\n");
								joinToTheNetworkFlag = true;

							}
						}

						pthread_mutex_unlock(&NeighborList);	
						printf("6 - Read Thread EXIT at socket = %d\n",sockDesc);
						
						pthread_exit(NULL);
					}
					recMessageBuffer[strlen(recMessageBuffer)+1] = '\0';
					//printf("Message Received = %s\n",recMessageBuffer);

					
					if(recHeader.messageType == notifyMessageType)
					{
						
						Messages msg;
						msg.Header = (char *)malloc(28*sizeof(char));
						memset(msg.Header,'\0',28);
						msg.Data = (char *)malloc(512*sizeof(char));
						memset(msg.Data,'\0',512);
						memcpy(msg.Header, &recHeader, 27);
						memcpy(msg.Data, &recMessageBuffer,recHeader.dataLength);
						struct timeval t;
						gettimeofday(&t, NULL);
						enterLogMessage(msg,'r',t);

						//notify received 
						pthread_mutex_lock(&WriteQueue[sockDesc]);
						pthread_cond_signal(&condition_var_Write[sockDesc]);						//signal write thread to awake if it is on condition variable						
						pthread_mutex_unlock(&WriteQueue[sockDesc]);
																													
						pthread_mutex_lock(&deleteConnectionLock[sockDesc]);													//lock
						deleteConnection[sockDesc] = true;							//set flag to tell keepalive and write to exit
						pthread_mutex_unlock(&deleteConnectionLock[sockDesc]);											//unlock
									

																											//remove node from neighbor list
						pthread_mutex_lock(&NeighborList);																//Apply LOCK
						multimap<string,int> tempMultiMap = node.getNeighborList();
						multimap<string,int>::iterator it;
						for(it = tempMultiMap.begin(); it!=tempMultiMap.end(); it++)
						{
							if((*it).second == sockDesc)
							{
								tempMultiMap.erase(it);
							}
						}
						node.setNeighborList(tempMultiMap);
						if(IsBeaconNode == false)
						{
							int totalNeighbors = node.getNeighborList().size();
							if( totalNeighbors < (int)node.getMinNeighbors())								
							{
								printf("joinToTheNetworkFlag is SET - 4\n");
								joinToTheNetworkFlag = true;

							}
						}
						pthread_mutex_unlock(&NeighborList);																//Release LOCK

						//check if active == false
						//check if isbecaon = false
						//nocheck == false
						if( checkActive == false && checkFlag == false && node.getNoCheck() == false)
						{
							pthread_create(&checkThread,NULL,checkThreadFunction,NULL);
						}
						

						
						printf("7 - read thread EXIT due to notify msg received\n");
						pthread_exit(NULL);
					}

					
					Messages msg;
					msg.socketDescripter = sockDesc;
					msg.Header = (char*)malloc(28*sizeof(char));
					memcpy(msg.Header,recHeaderBuffer,28);
					msg.Data = (char*)malloc(recHeader.dataLength*sizeof(char));
					memcpy(msg.Data,&recMessageBuffer,recHeader.dataLength);
					

					pthread_mutex_lock(&ReadQueue);																//APPLY LOCK
					
					readQueue.push_back(msg);															// msg added to READ QUEUE WITHOUT ntohs
					pthread_cond_signal(&condition_var_Read);	
					//printf("Read Queue Size = %d\n",readQueue.size());
					
					pthread_mutex_unlock(&ReadQueue);															//Release LOCK

				}
		}

	return NULL;
}

